package com.knowledge.share.service.websocket;

import cn.hutool.core.util.ObjectUtil;
import com.knowledge.share.config.GetHttpSessionConfig;
import com.knowledge.share.pojo.WsClientInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.PathVariable;

import javax.servlet.http.HttpSession;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
@CrossOrigin(origins = "*")
@ServerEndpoint(value = "/ws/{token}",configurator = GetHttpSessionConfig.class)
public class WebsocketUtils {

    /**
     * @desc 定义连接客户端对象信息的map映射关系结构
     * WsClientInfo 客户端连接存储的对象
     */
    private static final Map<String,WsClientInfo> wsClientInfoMap  =new ConcurrentHashMap<String, WsClientInfo>();

    //定义连接状态存活的时间，暂定30分钟
    private static final  int EXIST_TIME=30;
    /**
     * @desc websocket 连接建立成功的时候调用的方法，使用注解实现
     * @param session 连接的session值信息
     * @param sec
     * @param token 客户端传递过来的token信息，可以是用户名或者登录用户的token信息
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig sec, @PathParam("token") String token){
        //判断是否连接上了信息
        if(wsClientInfoMap.containsKey(token)){
            new RuntimeException(token+"客户端已经连接过了，不要重复连接");
        }

        //不存在那么实例化，把连接客户端的基本信心存储下来，存储到线程安全的对象中去
        WsClientInfo wsClientInfo = new WsClientInfo();
        wsClientInfo.setToken(token);
        wsClientInfo.setSession(session);
        wsClientInfo.setExistTime(LocalDateTime.now().plusMinutes(EXIST_TIME));
        wsClientInfoMap.put(token,wsClientInfo);//存储到对象中去
        log.info("websocket连接成功,{}",wsClientInfo);
        WsClientInfo entity = wsClientInfoMap.get(token);
        entity.getSession().getAsyncRemote().sendText(token+",恭喜你链接成功");
    }

    /**
     * 监听链接关闭的的事件信息
     * @param session
     * @param token
     */
    @OnClose
    public void onClose(Session session,@PathParam("token") String token){
        //找到链接关闭的用户并且直接从链接对象中删除
        if(ObjectUtil.isNotEmpty(token) && wsClientInfoMap.containsKey(token)){
            wsClientInfoMap.remove(token);
            log.info("{},断开了websocket链接",token);
        }
    }

    /**
     * @desc 监听链接错误的信息内容
     * @param error
     */
    @OnError
    public void onError(Throwable error){
        log.error("websocket链接错误:{}",error);
        error.printStackTrace();
    }
    /**
     * @desc 监听客户端发送过来的消息
     * @param session
     * @param token
     * @param message
     */
    @OnMessage
    public void onMessage(Session session,@PathParam("token") String token,String message){
        log.info("客户端传递过来的消息是:{}",message);
        //心跳检测机制，反馈给用户一个基本信息
        WsClientInfo wsClientInfo = wsClientInfoMap.get(token);

        if(LocalDateTime.now().compareTo(wsClientInfo.getExistTime())>0){
            log.info("链接状态已经过期直接关闭");
            wsClientInfoMap.remove(token);
        }

        //心跳检测机制，通过传递过来的用户，我们给用户一个反馈
        if("ping".equals(message)){


            wsClientInfo.setExistTime(LocalDateTime.now().plusMinutes(EXIST_TIME));

            if(wsClientInfo.getSession().isOpen()){
                wsClientInfo.getSession().getAsyncRemote().sendText("pang");
            }

        }
    }
}
